home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 329_01 / cflow.c < prev    next >
C/C++ Source or Header  |  1989-10-16  |  17KB  |  706 lines

  1. /*  >  cFlow.c
  2.  *
  3.  *  cFlow -- Print function dependency tree
  4.  *           from multiple C source files
  5.  *  (C) October 18  1989  Asaf Arkin
  6.  *  All rights reserved
  7.  */
  8.  
  9.  
  10.  
  11. /*  Include files:
  12.  */
  13.  
  14. #include  <ctype.h>
  15. #include  <stdarg.h>
  16. #include  <stdio.h>
  17. #include  <stdlib.h>
  18. #include  <string.h>
  19.  
  20.  
  21. /*  Macro constants:
  22.  *    True/False; function state mask bits; constant values.
  23.  */
  24.  
  25. #define  TRUE  1
  26. #define  FALSE  0
  27.  
  28. #define  DEFINED  01    /*  State: function defined          */
  29. #define  PRINTED  02    /*  State: function already printed  */
  30.  
  31. #define  ID_MAX  512    /*  Maximum identifier length  */
  32. #define  IND_ADD  2     /*  Indentation increment      */
  33.  
  34.  
  35. /*  Function declarations:
  36.  */
  37.  
  38. int   Error(char *, ...);
  39. void  Syntax(void);
  40. void  Process(char *);
  41. void  SkipQuote(FILE *, int *, int);
  42. void   DoFunc(int, FILE *, int *, int);
  43. void  *ScanFunc(char *);
  44. int   GetChar(FILE *, int *);
  45. void  DumpFlow(char *);
  46. void  DumpFunc(void *, int, int);
  47.  
  48.  
  49. /*  Structure strFunc: holds a function's state, line number, filename
  50.  *  offset, pointer to definition text, and offsets to invoked functions.
  51.  */
  52.  
  53. typedef struct
  54. {
  55.   char      State;
  56.   int       LineNumber;
  57.   unsigned  FileName;
  58.   char     *Definition;
  59.   unsigned  CallCnt;
  60.   unsigned  Calls;
  61. }  strFunc;
  62.  
  63.  
  64. /*  Structure strList: holds an offset to function's name and a pointer to
  65.  *  function's strFunc structure.
  66.  */
  67.  
  68. typedef struct
  69. {
  70.   int       Name;
  71.   strFunc  *Struct;
  72. }  strList;
  73.  
  74.  
  75. /*  Static variables:
  76.  *    Memory blocks: ID, File, Name, List.
  77.  *    Pointer to Func block of currently defined function.
  78.  *    Undefined (options -u) and include (option -i) flags.
  79.  *    cFlow output stream.
  80.  */
  81.  
  82. char     *IDName;
  83. int       IDLen;
  84. char     *FileBase = NULL;
  85. int       FileLen = 0;
  86. char     *NameBase = NULL;
  87. int       NameLen = 0;
  88. strList  *ListBase = NULL;
  89. int       ListLen = 0;
  90. strFunc  *CurFunc = NULL;
  91. char      UnDefined = 0,  Include = FALSE;
  92. FILE     *OutFile = stdout;
  93.  
  94.  
  95.  
  96. /*  int  main(int, char **)
  97.  *
  98.  *  Parse command line off options. That done, consume filenames from command
  99.  *  line, processing them with Process(). Finally, print dependency tree.
  100.  */
  101.  
  102. int  main(int Argc, char **Argv)
  103. {
  104.   int  Arg,  Offset = 0;
  105.   char  *Ptr;
  106.   char  *MainFunc = "main";
  107.  
  108.   /*  Print cFlow title. Allocate memory for ID (constant size) and quit if
  109.    *  failed to. In case of no command-line arguments, print syntax of cFlow
  110.    *  and quit.
  111.    */
  112.   printf("cFlow   (C) Oct 17 '89  by Asaf Arkin\n\n");
  113.   if (( IDName=malloc(ID_MAX) )==NULL)
  114.     return  Error("Cannot allocate memory to begin with");
  115.   if (Argc<2)
  116.   {
  117.     Syntax();
  118.     return  0;
  119.   }
  120.   for (Arg=1; Arg<Argc; ++Arg)
  121.   {
  122.     Argv[Arg]=Argv[Arg+Offset];
  123.     if (Argv[Arg][0]=='-')
  124.     {
  125.       for (Ptr=Argv[Arg]+1; *Ptr; ++Ptr)
  126.         switch (tolower(*Ptr))
  127.         {
  128.           case 'i':
  129.             Include=TRUE;
  130.             break;
  131.           case 'm':
  132.             if (!*++Ptr)
  133.             {
  134.               if (Arg+1<Argc)
  135.               {
  136.                 --Argc;
  137.                 Ptr=Argv[Arg+ ++Offset];
  138.               }
  139.               else
  140.               {
  141.                 Error("-m<main>  First function in report is <main>\n");
  142.                 break;
  143.               }
  144.             }
  145.             MainFunc=Ptr;
  146.             while (*++Ptr)  ;
  147.             --Ptr;
  148.             break;
  149.           case 'o':
  150.             if (!*++Ptr)
  151.             {
  152.               if (Arg+1<Argc)
  153.               {
  154.                 --Argc;
  155.                 Ptr=Argv[Arg+ ++Offset];
  156.               }
  157.               else
  158.               {
  159.                 Error("-o<file>  Send output to <file>");
  160.                 break;
  161.               }
  162.             }
  163.             if (( OutFile=freopen(Ptr,"w",OutFile) )==NULL)
  164.               return  Error("Cannot open file '%s' for output",Ptr);
  165.             while (*++Ptr)  ;
  166.             --Ptr;
  167.             break;
  168.           case 'u':
  169.             ++UnDefined;
  170.             break;
  171.           case '-':
  172.             break;
  173.           default:
  174.             Error("Option -%c unknown",*Ptr);
  175.         }
  176.       --Argc;
  177.       --Arg;
  178.       ++Offset;
  179.     }
  180.   }
  181.   for (Arg=1; Arg<Argc; ++Arg)
  182.     Process(Argv[Arg]);
  183.   DumpFlow(MainFunc);
  184.   return  0;
  185. }
  186.  
  187.  
  188. /*  int  Error(char *, ...)
  189.  *
  190.  *  Issue an error (printf-style arguments) and return zero.
  191.  */
  192.  
  193. int  Error(char *Message, ...)
  194. {
  195.   va_list  Args;
  196.  
  197.   va_start(Args,Message);
  198.   printf("cFlow: ");
  199.   vprintf(Message,Args);
  200.   printf(".\n");
  201.   va_end(Args);
  202.   return  0;
  203. }
  204.  
  205.  
  206. /*  void  Syntax(void)
  207.  *
  208.  *  Print cFlow's syntax.
  209.  */
  210.  
  211. void  Syntax(void)
  212. {
  213.   char  *Message =
  214.     "Syntax:   cFlow  [<file>|-i|-m<main>|-o<file>|-u|-v]...\n"
  215.     "Options:  -i        Read include files\n"
  216.     "          -m<main>  First function in report is <main>\n"
  217.     "          -o<file>  Send output to <file>\n"
  218.     "          -u        Show undefined functions (2 levels)\n\n";
  219.  
  220.   printf("%s",Message);
  221. }
  222.  
  223.  
  224.  
  225. /*  void  Process(char *)
  226.  *
  227.  *  Parse function definitions and invokations from the given C source file
  228.  *  and build from it the dependency tree.
  229.  */
  230.  
  231. void  Process(char *FileName)
  232. {
  233.   int  c,  LineCnt = 0,  Level = 0;
  234.   char  *Ptr,  *EndPtr;
  235.   FILE  *File;
  236.  
  237.   if (( File=fopen(FileName,"r") )==NULL)
  238.   {
  239.     Error("Cannot open source file '%s'",FileName);
  240.     return;
  241.   }
  242.   /*  If file has not been openned before, append its name to File block,
  243.    *  else quit (no point in reading a file twice.) Set FileName to the
  244.    *  filename's offset within File.
  245.    */
  246.   Ptr=FileBase;
  247.   EndPtr=Ptr+FileLen;
  248.   for (; Ptr<EndPtr; Ptr+=strlen(Ptr)+1)
  249.     if (!strcmp(FileName,Ptr))
  250.       return;
  251.   if (( Ptr=realloc(FileBase, FileLen+strlen(FileName)+1) )!=NULL)
  252.   {
  253.     FileName=strcpy(Ptr+FileLen,FileName);
  254.     FileBase=Ptr;
  255.     FileLen+=strlen(FileName)+1;
  256.   }
  257.   /*  Flush ID and start parsing the source file.
  258.    */
  259.   IDLen=0;
  260.   c=GetChar(File,&LineCnt);
  261.   while (!feof(File))
  262.   {
  263.     if (c==' ')
  264.     {
  265.       /*  Skip spaces, tabs and line delimiters.
  266.        */
  267.       c=GetChar(File,&LineCnt);
  268.       continue;
  269.     }
  270.     if (isalpha(c) || c=='_' || c=='*')
  271.     {
  272.       /*  Identifier: consume as many characters as possible into ID. *'s
  273.        *  are read because they are part of definitions (pointer to..).
  274.        */
  275.       while (c=='*')
  276.       {
  277.         IDName[IDLen++]=c;
  278.         c=GetChar(File,&LineCnt);
  279.       }
  280.       if (isalpha(c) || c=='_')
  281.       {
  282.         do
  283.         {
  284.           IDName[IDLen++]=c;
  285.           c=GetChar(File,&LineCnt);
  286.         }
  287.         while (isalnum(c) || c=='_');
  288.         IDName[IDLen++]=' ';
  289.       }
  290.       continue;
  291.     }
  292.     if (c=='(' && IDLen)
  293.     {
  294.       /*  Openning parentheses: could be a function call/definition.
  295.        */
  296.       DoFunc(FileName-FileBase,File,&LineCnt,Level);
  297.       IDLen=0;
  298.       c=GetChar(File,&LineCnt);
  299.       continue;
  300.     }
  301.     /*  Skip literal strings and character constants.
  302.      *  Count left and right braces to determine nesting level.
  303.      */
  304.     if (c=='\'' || c=='\"')
  305.       SkipQuote(File,&LineCnt,c);
  306.     else
  307.     if (c=='{')
  308.       ++Level;
  309.     else
  310.     if (c=='}')
  311.       if (--Level<0)
  312.         Error("Source error: too many }'s do not balance");
  313.     IDLen=0;
  314.     c=GetChar(File,&LineCnt);
  315.   }
  316.   /*  File parsed through. Close it an return.
  317.    */
  318.   if (fclose(File))
  319.     Error("Source file '%s' not closed",FileName);
  320. }
  321.  
  322.  
  323. /*  void  SkipQuote(FILE *, int *, int)
  324.  *
  325.  *  Skip literal strings ("...") and character constants ('.').
  326.  */
  327.  
  328. void  SkipQuote(FILE *File, int *LineCnt, int c)
  329. {
  330.   int  Quote;
  331.  
  332.   Quote=c;
  333.   do
  334.   {
  335.     c=getc(File);
  336.     if (c=='\n' || c=='\r' || c=='\v' || c=='\f')
  337.       ++*LineCnt;
  338.     else
  339.     if (c=='\\')
  340.     {
  341.       c=getc(File);
  342.       if (c=='\n')
  343.         ++*LineCnt;
  344.       else
  345.         c=getc(File);
  346.     }
  347.   }
  348.   while (c!=Quote && !feof(File));
  349. }
  350.  
  351.  
  352. /*  int  GetChar(FILE *, int *)
  353.  *
  354.  *  Read a character from the source file: whitespaces, line delimiters and
  355.  *  comments read as spac